package com.lazyframework.generator.core;

import com.lazyframework.commons.util.Maps;
import com.lazyframework.generator.model.Column;
import com.lazyframework.generator.model.Table;
import freemarker.template.Configuration;
import freemarker.template.Template;
import freemarker.template.TemplateException;
import lombok.extern.slf4j.Slf4j;

import java.io.*;
import java.nio.charset.StandardCharsets;
import java.util.List;
import java.util.Map;

/**
 * 抽象Provider，提供生成代码的一些基本操作。
 * <p>
 * Create by lazy in 2020/07/26
 */
@Slf4j
public abstract class AbstractGenerateProvider implements GenerateProvider {

    private static Configuration freemarkerConfiguration;

    /**
     * 输出文件的根目录
     */
    private String rootPath;

    protected static final String PROJECT_JAVA_DIC = "/src/main/java/";
    protected static final String PROJECT_RESOURCES_DIC = "/src/main/resources/";

    public AbstractGenerateProvider(String rootPath) {
        this.rootPath = rootPath;
    }

    public AbstractGenerateProvider() {
        this(System.getProperty("user.home") + "/Generator/");
    }

    @Override
    public void generate(Table table, String projectName, String basePackage, String module, String domainObjName) {
        String comment = table.getComment();
        List<Column> columns = table.getColumns();

        // 主键ID检查 (生成代码的表必须包含主键ID)
        if (columns.stream()
                .noneMatch(column -> column.getName().equalsIgnoreCase("id"))) {
            log.error("Generate failed, id column is missing!");
            return;
        }

        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~生成表单对象~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
        Map<String, Object> formParams = Maps.<String, Object>builder()
                .put("package", basePackage + ".web." + module + ".form")
                .put("comment", comment + "表单对象")
                .put("clsName", domainObjName + "Form")
                .put("columns", columns)
                .build();
        printJava(projectName, "form.ftl", formParams);

        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~生成Model对象~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
        Map<String, Object> modelParams = Maps.<String, Object>builder()
                .put("package", basePackage + ".model." + module)
                .put("comment", comment + "Model对象")
                .put("clsName", domainObjName + "Model")
                .put("columns", columns)
                .build();
        printJava(projectName, "model.ftl", modelParams);

        // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~调用子类方法生成持久层对象（框架不同，代码不同）~~~~~~~~~~~~~~~~~~~~~~~~~~~ //
        doInternalGenerate(table, projectName, basePackage, module, domainObjName);
    }

    /**
     * 由子类根据所选定的持久层框架完成持久层代码的生成，父类只负责完成持久层无关的Form对象和Model对象的生成
     *
     * @param table         Table对象
     * @param projectName   项目名称
     * @param basePackage   包名
     * @param module        模块名
     * @param domainObjName 表对象名称
     */
    protected abstract void doInternalGenerate(Table table,
                                               String projectName, String basePackage, String module, String domainObjName);

    /**
     * 生成Java文件
     *
     * @param projectName 项目名称
     * @param template    模板文件
     * @param params      参数对象
     */
    protected void printJava(String projectName, String template, Map<String, Object> params) {
        String packagePath = params.get("package").toString().replace(".", "/");
        String outPath = getRootPath() + projectName + PROJECT_JAVA_DIC + packagePath;
        print(template, params, outPath, params.get("clsName") + ".java");
    }

    /**
     * 渲染模板并输出到文件系统
     *
     * @param templateName 模板文件名称
     * @param params       渲染模板所需的参数对象
     * @param outPath      输出目录
     * @param outFile      输出的文件名
     */
    protected static void print(String templateName, Map<String, Object> params, String outPath, String outFile) {
        File targetDic = new File(outPath);
        if (!targetDic.exists()) {
            targetDic.mkdirs();
        }
        try (FileOutputStream fos = new FileOutputStream(new File(targetDic + "/" + outFile));
             BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(fos, StandardCharsets.UTF_8))) {
            Template template = freemarkerConfiguration.getTemplate(templateName);
            template.process(params, writer);
        } catch (IOException | TemplateException e) {
            e.printStackTrace();
        }
    }

    /**
     * 输出文件的根目录
     *
     * @return
     */
    protected String getRootPath() {
        return this.rootPath;
    }

    static {
        freemarkerConfiguration = new Configuration(Configuration.DEFAULT_INCOMPATIBLE_IMPROVEMENTS);
        freemarkerConfiguration.setClassForTemplateLoading(GenerateProvider.class, "/ftl");
    }
}
